/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.upf.grupoe.p2ppicture.DBAccessEngine.implementations;


import edu.upf.grupoe.p2ppicture.DBAcessEngine.Interfaces.DBManager;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;
import org.exist.xmldb.XQueryService;
import org.xmldb.api.modules.XUpdateQueryService;
import org.exist.xmldb.DatabaseImpl;


// llibreries que cal posar
/*
import org.exist.xmldb.XQueryService;
import org.xmldb.api.base.Collection;
*/

/**
 *
 * @author Alberto Llor
 * NIa 69808
 */



public class DBManagerImpl implements DBManager {

    private String user;
    private String password;
    private String defaultQueryService;
    private String defaultQueryServiceVersion;
    private String defaultQueryServiceUpdated;
    private String defaultQueryServiceUpdatedVersion;
    private XMLDBDatasourceImpl datasource;
    public static Log logger=LogFactory.getLog("Main");
    private static DBManagerImpl INSTANCE;
    
    private static void createInstance() {
        if (INSTANCE == null) {
            INSTANCE = new DBManagerImpl();
        }
    }
    /**
     * DBManager Singleton
     * @return DBManagerImpl
     */
    public static DBManagerImpl getInstance() {
        if (INSTANCE == null) createInstance();
        return INSTANCE;
    }
    
    public DBManagerImpl() {
        
        user = "";
        password = "";
        
        defaultQueryService="XQueryService";
        defaultQueryServiceVersion="1.0";
        defaultQueryServiceUpdated="XUpdateQueryService";
        defaultQueryServiceUpdatedVersion="1.0";
        
    }
    
    public DBActionResultImpl execute(DBActionImpl action) {
        if(logger.isDebugEnabled()){
            logger.info("execute entry");
        }
        DBActionResultImpl ar=null;
        org.xmldb.api.base.Collection c=null;
        XQueryService qs=null;
        XUpdateQueryService us=null;
        ResourceSet result=null; 
        long updateResult=0;
        String query="";
        // Retrieving the Collection
        try {
            datasource = new XMLDBDatasourceImpl();
            datasource.startDBConnection();
            c = (org.xmldb.api.base.Collection)datasource.getCollection(datasource.getUser(),datasource.getPassword());
            if(logger.isDebugEnabled()){
                logger.info("A Collection has been created!");
            }
        } catch (XMLDBException e) {
            ar=new DBActionResultImpl(false);
            ar.setErrorMsg("An error happened while accessing on getCollection(String,String)");
            ar.setException(e);
            if(logger.isDebugEnabled())
                logger.error("An error happened while accessing on getCollection(String,String)");
        }
        
        // Query translation
        try {
            query=translateQuery(action);
            if(logger.isDebugEnabled()){
                logger.info("Query translated");
                logger.info(action.getActionId());
            }

            
            try {
                qs=(XQueryService)c.getService(defaultQueryService, defaultQueryServiceVersion);
            } catch (XMLDBException ex) {
                if(logger.isDebugEnabled()){
                    logger.error("Error getting the service up: "+ex.errorCode + " "+ex.vendorErrorCode);
                }
            }
            
        } catch (IOException ioe) {
            ar=new DBActionResultImpl(false);
            ar.setErrorMsg("There was not possible to access the file 'queries.properties'. Review it"+ ioe);
            ar.setException(ioe);
            // and we print the message error into the log
            if(logger.isDebugEnabled()){
                logger.error("There was not possible to access the file 'queries.properties'. Review it");
            }
        } catch (NullPointerException npe) {
            ar=new DBActionResultImpl(false);
            ar.setErrorMsg("There were not parameters found in queries");
            ar.setException(npe);
            //and we print the message error into the log
            if(logger.isDebugEnabled()){
                logger.error("There was an error while trying to obtain the services", npe);
            }
        }
        
        // Query case
        if(action.getActionId().equals("query.freeSearchPicture")){
            logger.info("query");
            try {
                if (!(query.equals("") && (ar==null))) {
                // QUERY
                    if(logger.isDebugEnabled()){
                        logger.info("Query to search: "+query);
                    }
                    try{
                            result=qs.query(query);
                    }
                    catch(NullPointerException e){
                        if(logger.isDebugEnabled()){
                            logger.error("Error querying: "+e.getMessage());
                        }
                    }
                    if (null!=result) {
                        ar=new DBActionResultImpl(true);

                        List<Photo> photos=new ArrayList();

                        PhotoHandler h=new PhotoHandler();

                        ResourceIterator i = result.getIterator();
                            while(i.hasMoreResources()) {
                                    XMLResource r = (XMLResource) i.nextResource();
                                    Photo ph = new Photo();
                                    PhotoHandler handler = new PhotoHandler(ph);
                                    r.getContentAsSAX(handler);
                                    photos.add(ph);
                            }

                        ar.setResult(photos);
                        if(logger.isDebugEnabled()){
                            logger.debug("Query got: "+photos.toString());
                        }
                    }
                    else {
                        if(logger.isDebugEnabled()){
                            logger.info("Empty result");
                        }
                        ar=new DBActionResultImpl(false);
                        ar.setErrorMsg("Empty query");
                        ar.setException(new Exception("Empty Query"));
                        if(logger.isDebugEnabled()){
                            logger.error("Empty query");
                        }
                    }
            }        
            } catch (Exception e) {
                ar=new DBActionResultImpl(false);
                ar.setErrorMsg ("Error");
                ar.setException(e);
                // and we print the error into the log
                if(logger.isDebugEnabled()){
                    logger.error("Error doing the query", e);
                    }
            }
        }
        // Update 
        if(action.getActionId().equals("addPicture")){
            logger.info("update");
            try {
                if (!(query.equals("") && (ar==null))) {
                // QUERY
                    if(logger.isDebugEnabled()){
                        logger.info("Query to update: "+query);
                    }
                    try{
                            result=qs.query(query);
                    }
                    catch(NullPointerException e){
                        if(logger.isDebugEnabled()){
                            logger.error("Error querying: "+e.getMessage());
                        }
                    }
            }        
            } catch (Exception e) {
                ar=new DBActionResultImpl(false);
                ar.setErrorMsg ("Error");
                ar.setException(e);
                // and we print the error into the log
                if(logger.isDebugEnabled()){
                    logger.error("Error doing the query", e);
                    }
            }
        }
        
        // we return the modified value of the ar
        return ar;
    }
        
    private Properties setConfiguration() throws IOException {
        // we declare the needed private variables
        final Properties p=new Properties();
        final InputStream is=XMLDBDatasourceImpl.class.getClassLoader().getResourceAsStream("queries.properties");
        InputStreamReader isr=null;
        String utf8="UTF-8";
        try {
            isr=new InputStreamReader(is, utf8 );
            p.load(isr);
        } finally {
            // we close  the isr that are not closed
            if (isr!=null) {
                isr.close();
            }
            // and the same with the input stream
            if (is!=null)  {
                is.close();
            }
            return p;
        }   
    }        
        
        private String translateQuery (DBActionImpl action)throws IOException, NullPointerException {
            String id=action.getActionId();
            String outputQuery="";
            Properties p=null;
            // Queries from queries.properties
            p=this.setConfiguration();
            if (null!=p) {
                String nullQuery=p.getProperty(id);
                if(logger.isDebugEnabled()){
                    logger.debug("Null query: " + nullQuery);
                }
            if (null != nullQuery) {
                if(logger.isDebugEnabled()){
                    logger.info("pattern: "+nullQuery);
                }
                outputQuery = MessageFormat.format(nullQuery, action.getParameters()); 
                if(logger.isDebugEnabled()){
                    logger.debug("Query: " + outputQuery);
                }
            } else {
                if(logger.isDebugEnabled()){
                    logger.error("Action: " + id + " not found into Database");
                }
            }
        } else {
            if(logger.isDebugEnabled()){    
                logger.error("Database actions has not been successfully loaded");
            }
        }
        return outputQuery;
        }   
        
        public XMLDBDatasourceImpl getDatasource(){
            return datasource;
        }
}